package com.logica.tutorial.web;

import java.util.Arrays;
import java.util.List;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormValidatingBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.WindowClosedCallback;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.SimpleFormComponentLabel;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.logica.tutorial.dto.PersonDTO;
import com.logica.tutorial.model.Parent;
import com.logica.tutorial.model.Person;
import com.logica.tutorial.model.Sex;
import com.logica.tutorial.service.PersonService;
import com.logica.tutorial.soap.Post;
import com.logica.tutorial.soap.ZipCode;

public class HomePage extends Panel {
	private static final long serialVersionUID = 241427784832598573L;
	@SpringBean
	private PersonService personService;
	private FormComponent<?> idField;
	private FormComponent<?> fatherField;
	private FormComponent<?> motherField;
	private FormComponent<?> cityField;
	private FormComponent<?> streetField;
	private FormComponent<?> zipCodeField;
	private Person father;
	private Person mother;
	private Person person;
	private Sex sex = Sex.NA;
	private Form<Person> form;
	private AreYouSurePanel areYouSurePanel;
	private SelectPersonPanel selectPersonPanel;
	private Parent fieldToFill = Parent.FATHER;
	private final FeedbackPanel feedback;
	private WebMarkupContainer zipCodePanel;
	private LoadableDetachableModel zipCodeList;
	private WebMarkupContainer personListContainer;
	private LoadableDetachableModel personList;

	public Sex getSex() {
		return sex;
	}

	private ModalWindow modal;

	public void setParent(Person parent) {
		if (fieldToFill == Parent.FATHER)
			father = parent;
		else
			mother = parent;
	}

	public HomePage(String id) {
		super(id);

		// create feedback panel to show errors
		feedback = new FeedbackPanel("feedbackMain");
		feedback.setOutputMarkupId(true);
		add(feedback);

		person = new Person();
		CompoundPropertyModel personModel = new CompoundPropertyModel(person);

		zipCodePanel = new WebMarkupContainer("zipCodePanel");
		zipCodePanel.setOutputMarkupId(true);
		zipCodePanel.setOutputMarkupPlaceholderTag(true);
		zipCodePanel.setVisible(false);
		add(zipCodePanel);

		form = new Form<Person>("form", personModel);
		add(form);
		form.setOutputMarkupId(true);

		FormComponent<?> fc;

		idField = new TextField<Long>("id").setRequired(true);
		idField.setLabel(new ResourceModel("label.Id"));
		idField.add(new PersonIdValidator());
		idField.setOutputMarkupId(true);
		form.add(idField);
		form.add(new SimpleFormComponentLabel("id-label", idField));
		fc = new TextField<String>("firstName")
				.setConvertEmptyInputStringToNull(false);
		fc.setLabel(new ResourceModel("label.firstName"));
		form.add(fc);
		form.add(new SimpleFormComponentLabel("firstName-label", fc));

		fc = new TextField<String>("lastName")
				.setConvertEmptyInputStringToNull(false);
		fc.setLabel(new ResourceModel("label.lastName"));
		form.add(fc);
		form.add(new SimpleFormComponentLabel("lastName-label", fc));

		List<Sex> sexes = Arrays.asList(new Sex[] { Sex.MALE, Sex.FEMALE,
				Sex.NA });

		fc = new DropDownChoice<Object>("sex", sexes,
				new IChoiceRenderer<Object>() {
					public Object getDisplayValue(Object methodObj) {
						// Localize / Show user friendly strings
						return getString(methodObj.toString());
					}

					public String getIdValue(Object id, int index) {
						return id.toString();
					}
				});
		fc.setLabel(new ResourceModel("label.sex"));
		form.add(fc);
		form.add(new SimpleFormComponentLabel("sex-label", fc));

		fatherField = new TextField<Person>("father", new Model());
		fatherField.setLabel(new ResourceModel("label.father"));
		fatherField.add(new AjaxEventBehavior("onfocus") {
			@Override
			protected void onEvent(AjaxRequestTarget target) {
				fieldToFill = Parent.FATHER;
				selectPersonPanel.setParentField(fatherField);
				selectPersonPanel.setParentSex(Sex.MALE);
				modal.show(target);
			}
		});

		form.add(fatherField);
		form.add(new SimpleFormComponentLabel("father-label", fatherField));

		motherField = new TextField<Person>("mother", new Model());
		motherField.setLabel(new ResourceModel("label.mother"));
		motherField.add(new AjaxEventBehavior("onfocus") {
			@Override
			protected void onEvent(AjaxRequestTarget target) {
				fieldToFill = Parent.MOTHER;
				selectPersonPanel.setParentField(motherField);
				selectPersonPanel.setParentSex(Sex.FEMALE);
				modal.show(target);
			}
		});

		form.add(motherField);
		form.add(new SimpleFormComponentLabel("mother-label", motherField));

		streetField = new TextField<String>("street",
				personModel.bind("address.street"))
				.setConvertEmptyInputStringToNull(false);
		streetField.setLabel(new ResourceModel("label.street"));
		streetField.add(new UpdateZipCodesBehavior("onchange", this,
				zipCodePanel));
		streetField.add(new UpdateZipCodesBehaviorDelayed("keyup", this,
				zipCodePanel));
		form.add(streetField);
		form.add(new SimpleFormComponentLabel("street-label", streetField));

		fc = new TextField<String>("houseNr",
				personModel.bind("address.houseNr"))
				.setConvertEmptyInputStringToNull(false);
		fc.setLabel(new ResourceModel("label.houseNr"));
		form.add(fc);
		form.add(new SimpleFormComponentLabel("houseNr-label", fc));

		cityField = new TextField<String>("city",
				personModel.bind("address.city"))
				.setConvertEmptyInputStringToNull(false);
		cityField.setLabel(new ResourceModel("label.city"));
		cityField
				.add(new UpdateZipCodesBehavior("onchange", this, zipCodePanel));
		cityField.add(new UpdateZipCodesBehaviorDelayed("keyup", this,
				zipCodePanel));
		form.add(cityField);
		form.add(new SimpleFormComponentLabel("city-label", cityField));

		zipCodeField = new TextField<Integer>("zipCode",
				personModel.bind("address.zipCode"))
				.setConvertEmptyInputStringToNull(false);

		zipCodeField.setLabel(new ResourceModel("label.zipCode"));
		form.add(zipCodeField);
		form.add(new SimpleFormComponentLabel("zipCode-label", zipCodeField));

		form.add(new AjaxButton("main-clear-button", form) {
			@Override
			protected void onSubmit(final AjaxRequestTarget target, Form form) {
				clear();
				target.add(form);
			}
		}.setDefaultFormProcessing(false));

		AjaxFormValidatingBehavior.addToAllFormComponents(form, "onsubmit");

		// add a button that can be used to submit the form via ajax
		AjaxButton submitButton = new AjaxButton("ajax-button", form) {
			private static final long serialVersionUID = -6253267865337423089L;

			@Override
			protected void onSubmit(final AjaxRequestTarget target, Form<?> form) {
				// repaint the feedback panel so that it is hidden
				target.add(feedback);
				refreshTextFieldsErrorStatus(target);
				addPerson(target);
			}

			@Override
			protected void onError(final AjaxRequestTarget target, Form<?> form) {
				refreshTextFieldsErrorStatus(target);
				// repaint the feedback panel so errors are shown
				target.add(feedback);
			}
		};

		form.add(submitButton);

		add(modal = new ModalWindow("modal"));
		modal.setWindowClosedCallback(new WindowClosedCallback() {
			private static final long serialVersionUID = 1L;

			@Override
			public void onClose(AjaxRequestTarget target) {
				selectPersonPanel.hideResultList();
			}
		});
		modal.setInitialHeight(710);
		modal.setInitialWidth(650);
		modal.setResizable(false);
		selectPersonPanel = new SelectPersonPanel(modal.getContentId(), this,
				modal);
		modal.setContent(selectPersonPanel);
		modal.setTitle("Select a person");
		modal.setCookieName("selectPersonModal");

		personList = new LoadableDetachableModel() {
			protected Object load() {
				Post postService = new Post();
				return personService.findAllPersons();
			}
		};

		@SuppressWarnings("unchecked")
		final ListView personListView = new ListView("personList", personList) {
			@Override
			protected void populateItem(ListItem item) {
				final PersonDTO personItem = (PersonDTO) item.getModelObject();

				Label personItemIdLabel = new Label("personItemIdLabel",
						personItem.getId() + "");
				item.add(personItemIdLabel);
				Label personItemFirstNameLabel = new Label(
						"personItemFirstNameLabel", personItem.getFirstName());
				item.add(personItemFirstNameLabel);
				Label personItemLastNameLabel = new Label(
						"personItemLastNameLabel", personItem.getLastName());
				item.add(personItemLastNameLabel);
				Label personItemSexLabel = new Label("personItemSexLabel",
						personItem.getSex().toString());
				item.add(personItemSexLabel);
				Label personItemCityLabel = new Label("personItemCityLabel",
						personItem.getAddress().getCity());
				item.add(personItemCityLabel);
				Label personItemStreetLabel = new Label(
						"personItemStreetLabel", personItem.getAddress()
								.getStreet());
				item.add(personItemStreetLabel);
				Label personItemHouseNrLabel = new Label(
						"personItemHouseNrLabel", personItem.getAddress()
								.getHouseNr());
				item.add(personItemHouseNrLabel);

				String zipCodeOfPerson = "";
				if (personItem.getAddress().getZipCode() != null) {
					zipCodeOfPerson = personItem.getAddress().getZipCode()
							.toString();
				}
				Label personItemZipLabel = new Label("personItemZipLabel",
						zipCodeOfPerson);
				item.add(personItemZipLabel);
				
				AjaxLink link = new AjaxLink("removePersonLink") {
					public void onClick(AjaxRequestTarget target) {
						personService.removePerson(personService.getPersonById(personItem.getId()));
						target.add(personListContainer);
					}
				};
				link.setOutputMarkupId(true);
				link.add(new Label("removePersonLinkText", "Delete"));
				item.add(link);
			}
		};

		personListContainer = new WebMarkupContainer("personListContainer");
		personListContainer.setOutputMarkupId(true);
		personListContainer.setOutputMarkupPlaceholderTag(true);
		personListContainer.add(personListView);
		add(personListContainer);

		zipCodeList = new LoadableDetachableModel() {
			protected Object load() {
				Post postService = new Post();
				return postService
						.getPostSoap()
						.getZipCode(streetField.getValue(),
								cityField.getValue()).getZipCode();
			}
		};

		@SuppressWarnings("unchecked")
		final ListView zipCodeLinkView = new ListView("zipCodeList",
				zipCodeList) {
			@Override
			protected void populateItem(ListItem item) {
				final ZipCode zipCode = (ZipCode) item.getModelObject();
				AjaxLink link = new AjaxLink("zipCodeLink") {
					public void onClick(AjaxRequestTarget target) {
						zipCodeField.setDefaultModelObject(zipCode.getCode());
						System.out.println(zipCode.getCode());
						target.add(zipCodeField);
					}
				};
				link.setOutputMarkupId(true);
				link.add(new Label("zipCodeLinkText", zipCode.getCode() + ""));
				item.add(link);

				Label zipCodeLinklabel = new Label("zipCodeLinklabel", "- "
						+ zipCode.getHouses() + ", " + zipCode.getStreet()
						+ ", " + zipCode.getSettlement());
				item.add(zipCodeLinklabel);
			}
		};

		WebMarkupContainer zipCodeListContainer = new WebMarkupContainer(
				"zipCodeListContainer");

		zipCodeListContainer.setOutputMarkupId(true);
		zipCodeListContainer.setOutputMarkupPlaceholderTag(true);
		zipCodeListContainer.add(zipCodeLinkView);
		zipCodePanel.add(zipCodeListContainer);
	}

	public void updateZipCodeList() {
		System.out.println(streetField.getValue());
		System.out.println(cityField.getValue());
	}

	public void clear() {
		fatherField.setDefaultModelObject(null);
		motherField.setDefaultModelObject(null);
		mother = null;
		father = null;
		form.setModelObject(new Person());
		form.clearInput();
		zipCodePanel.setVisible(false);
	}

	public void addPerson(AjaxRequestTarget target) {
		Person personToAdd = (Person) form.getDefaultModelObject();
		personToAdd.setFather(father);
		personToAdd.setMother(mother);
		personToAdd.getAddress().setId(null); // hacky, else
												// "detached entity passed to persist",
												// address already has
												// some id set
		try {
			personService.addPerson(personToAdd);
			clear();
			target.add(zipCodePanel);
			target.add(form);
			target.add(personListContainer);
		} catch (Exception e) {
			if (e.getMessage().contains("IdFirstNrException")) {
				idField.error(getLocalizer().getString("exception.firstNr",
						HomePage.this));
			} else if (e.getMessage().contains("IdLengthException")) {
				idField.error(getLocalizer().getString("exception.IdLength",
						HomePage.this));
			} else if (e.getMessage().contains("IdBirthDateException")) {
				idField.error(getLocalizer().getString("exception.birthDate",
						HomePage.this));
			} else if (e.getMessage().contains("IdCheckSumException")) {
				idField.error(getLocalizer().getString("exception.checkSum",
						HomePage.this));
			}

			refreshTextFieldsErrorStatus(target);
			target.add(form);
			target.add(feedback);
		}

	}

	public void refreshTextFieldsErrorStatus(final AjaxRequestTarget target) {
		form.visitChildren(TextField.class, new IVisitor<Component, Void>() {
			@Override
			public void component(Component component, final IVisit<Void> visit) {
				if (!component.hasErrorMessage()) {
					component.add(new AttributeModifier("class", ""));
					target.add(component);
				} else {
					component.add(new AttributeModifier("class", "error"));
					target.add(component);
				}
			}
		});
	}
}
